/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package fr.loof.fonzie.parsers;

import java.util.List;
import java.util.ListIterator;

import fr.loof.fonzie.keywords.Equals;
import fr.loof.fonzie.keywords.WhereClauseDelimiter;

public class Property
    implements QueryElement, QueryPostProcessor
{
    private static KeyWord DEFAULT_CONSTRAINT = new Equals();

    private Class<?> entity;

    private String path;

    private Class<?> type;

    private Decorator decorator;

    public Property( Class<?> entity, String path, Class<?> type )
    {
        super();
        this.entity = entity;
        this.path = "." + path;
        this.type = type;
    }

    public void relativePath( Property relativePath )
    {
        path += relativePath.getPath();
        type = relativePath.getType();
    }

    public Class<?> getEntity()
    {
        return entity;
    }

    public String getPath()
    {
        return path;
    }

    public Class<?> getType()
    {
        if ( decorator != null )
        {
            return decorator.getType( type );
        }
        return type;
    }

    public void setDecorator( Decorator decorator )
    {
        this.decorator = decorator;
    }

    @Override
    public String toString()
    {
        String toString = entity.getSimpleName() + path + " (" + type.getSimpleName() + ")";
        if ( decorator != null )
        {
            return decorator.decorate( toString );
        }
        return toString;
    }

    public boolean postProcess( ListIterator<QueryElement> elements )
    {
        boolean changed = applyDecorator( elements );

        if ( hasNoConstraint( elements ) && inWhereClause( elements ) )
        {
            elements.add( DEFAULT_CONSTRAINT );
            return true;
        }
        return changed;
    }

    private boolean applyDecorator( ListIterator<QueryElement> elements )
    {
        if ( elements.hasNext() )
        {
            QueryElement next = elements.next();
            elements.previous();

            if ( next instanceof Decorator )
            {
                elements.remove();
                this.decorator = (Decorator) next;
                return true;
            }
        }
        return false;
    }

    @SuppressWarnings( "unchecked" )
    public void process( JPQLPersistenceQuery jpqlQuery, List<Class> params )
    {
        if ( decorator != null )
        {
            jpqlQuery.append( " " ).append( decorator.decorate( "e" + path ) );
        }
        else
        {
            jpqlQuery.append( " e" ).append( path );
        }
        jpqlQuery.setCurrentProperty( this );
    }

    private boolean hasNoConstraint( ListIterator<QueryElement> elements )
    {
        if ( !elements.hasNext() )
        {
            return true;
        }
        QueryElement next = elements.next();
        elements.previous();

        if ( next instanceof Constraint )
        {
            return false;
        }
        return true;
    }

    private boolean inWhereClause( ListIterator<QueryElement> elements )
    {
        boolean whereClause = true;
        int count = 0;
        while ( elements.hasPrevious() )
        {
            count++;
            QueryElement element = elements.previous();
            if ( element instanceof WhereClauseDelimiter )
            {
                whereClause = false;
                break;
            }
        }
        while ( count-- > 0 )
        {
            elements.next();
        }
        return whereClause;
    }

    public static interface Decorator
    {
        String decorate( String path );

        Class<?> getType( Class<?> type );
    }
}
